﻿using System;
using System.Linq;
using MathParser.DataModel;

namespace MathParser {
    public partial class Parser {

        private readonly CalculatorMapper calcMapper = new CalculatorMapper();

        public double Caluculate(double x) {

            double ergebnis = 0;

            if (OpTyp == Operation.Zahl) {
                ergebnis = double.Parse(Expression);
            }

            else if (Ist_binär_operation()){
                var wert1 = ChildLeft.Caluculate(x);
                var wert2 = ChildRight.Caluculate(x);
                if ((ChildLeft.Error == ErrorType.No) && (ChildRight.Error == ErrorType.No)) {

                    try {
                        foreach (var map in this.calcMapper.BinärMap.Where(map => OpTyp == map.Key)) {
                            ergebnis = map.Value(wert1, wert2);
                            break;
                        }
                    }
                    catch (Exception) {
                        Error = ErrorType.Berechnung;
                    }
                    if (double.IsNaN(ergebnis) || double.IsInfinity(ergebnis) ||
                        double.IsNegativeInfinity(ergebnis) || double.IsPositiveInfinity(ergebnis)) {
                        Error = ErrorType.Berechnung;
                    }
                }
                else {
                    Error = ErrorType.Berechnung;
                    ChildLeft.ClearError();
                    ChildRight.ClearError();
                }
            }

            else if (Ist_unäre_operation()) {
                try {
                    foreach (var map in this.calcMapper.UnärMap.Where(map => OpTyp == map.Key)) {
                        ergebnis = map.Value(ChildLeft, x);
                        break;
                    }
                }
                catch (Exception) {
                    Error = ErrorType.Berechnung;
                }
                if (double.IsNaN(ergebnis) || double.IsInfinity(ergebnis) ||
                    double.IsNegativeInfinity(ergebnis) || double.IsPositiveInfinity(ergebnis)) {
                    Error = ErrorType.Berechnung;
                }
            }

            else {
                foreach (var map in this.calcMapper.ConstMap.Where(map => OpTyp == map.Key)) {
                    ergebnis = map.Value(x);
                    break;
                }
            }

            return ergebnis;
        }

        internal static double CalcFakultät(double value) {
            double ergebnis;
            if (value >= 0) {
                if (value == 0 || value == 1) 
                    ergebnis = 1;
                else {
                    ergebnis = value;
                    for (long i = ((int)(value) - 1); i > 0; i--) {
                        ergebnis *= i;
                    }
                }
            }
            else {
                throw new ArgumentException("Parameter kleiner Null.");
            }
            return ergebnis;
        }
    }
}